package yun.web.activerecord.dialect.pg;

import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import org.postgresql.util.PGobject;
import yun.web.activerecord.ActiveRecordPlugin;
import yun.web.activerecord.DruidPlugin;
import yun.web.activerecord.Record;
import yun.web.activerecord.ext.ModelSerializer;
import yun.web.activerecord.ext.PDirective;
import yun.web.activerecord.ext.PGObjectSerializer;
import yun.web.activerecord.ext.RecordSerializer;
import yun.web.activerecord.generator.Generator;
import yun.web.activerecord.generator.MappingKitGenerator;
import yun.web.enjoy.kit.PathKit;
import yun.web.enjoy.kit.PluginKit;
import yun.web.enjoy.template.source.ClassPathSourceFactory;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

public class PGARP {
    public static void init(JSONObject it) throws Exception {

        DruidPlugin dp = getDruidPlugin(it); //druid_plugin
        dp.start();

        String arp_id = it.getString("id");

        ActiveRecordPlugin arp = new ActiveRecordPlugin(arp_id, dp); //arp_plugin
        arp.setDialect(new PostgreSqlDialect());
        boolean devMode = null == System.getProperty("devMode");
        arp.setDevMode(devMode); //是否为开发模式, 非开发模式会缓存sql_engine读取到的sql模板id, 一定程度提高效率;


        //不设置 ,或者为 true 时  , 添加表 和 model的 映射
        Boolean mapping = it.getBoolean("mapping");
        if (mapping == null || mapping) {
            Class mappingKitClass = Class.forName(it.getString("model_package_name") + "._MappingKit"); //添加 table 和 model的 映射
            Method method = mappingKitClass.getMethod("mapping", ActiveRecordPlugin.class); //
            method.invoke(null, arp);
        }

        //是否打印sql, sql中没有无参数
        arp.setShowSql(it.getBoolean("showsql"));

        //FastJson序列化全局配置对象
        SerializeConfig serializeConfig = SerializeConfig.getGlobalInstance();
        serializeConfig.put(Record.class, RecordSerializer.me); //配置JFinalRecord转JSON字符串规则
        serializeConfig.put(PGobject.class, PGObjectSerializer.me);

        String model_package_name = it.getString("model_package_name");
        if (model_package_name == null || "".equals(model_package_name)) {
            //logger.info("")
        } else {
            ClassUtil.scanPackage(model_package_name).forEach(modelClass -> {
                // 遍历model包下所有类, 排除BaseModel, 配置 JFInalModel转JSON字符串规则
                Type superclass = modelClass.getGenericSuperclass();
                if (superclass != null) {
                    String typeName = superclass.getTypeName();
                    //排除Base
                    if (typeName.contains("Base")) {
                        serializeConfig.put(modelClass, ModelSerializer.me);
                    }
                }
            });
        }

        arp.getSqlKit().getEngine() //每一个arp对象都有自己的sql engine
                .setSourceFactory(new ClassPathSourceFactory())  //需要sql engine管理sql文件时, 告诉它文件从哪里获取
                .removeDirective("p").addDirective("p", PDirective.class); // 自定义p指令

        String sql_template = it.getString("sql_template");
        if (sql_template != null) {
            arp.addSqlTemplate(sql_template);
        }
        arp.start();
        PluginKit.add(dp);
        PluginKit.add(arp);

        System.out.println("====  数据源 " + arp.getConfig().getName() + " 初始化完毕 ==== ");

    }



    public static void generateModel(JSONObject it) {
            //获取DbRecord所需参数
            String modelPackageName = it.getString("model_package_name");

            // BaseModel输出目录 设置为和 Model输出目录一致, 在schema目录内部 用base隔离
            String baseModelPackageName = modelPackageName; //+ ".base" // base ax.jshop.model 所使用的包名


            String out_put_root = PathKit.getWebRootPath() + "\\" + (it.get("out_put_root") == null ? "" : it.get("out_put_root"));

            String baseModelOutputDir = out_put_root + "/" + baseModelPackageName.replace(".", "/");

            //加载把并启动德鲁伊插件 ,这里设置初始化连接数为1
            it.put("initial_size", 1);
            DruidPlugin druidPlugin = getDruidPlugin(it);
            druidPlugin.start();

            //获取连接池
            DataSource ds = druidPlugin.getDataSource();

            // base_model生成器
            PGBaseModelGenerator pgBaseModelGenerator = new PGBaseModelGenerator(baseModelPackageName, baseModelOutputDir);

            // model生成器
            PGModelGenerator modelGenerator = new PGModelGenerator(modelPackageName, baseModelPackageName, baseModelOutputDir);
            modelGenerator.setTemplate("yun/web/activerecord/dialect/pg/model_template.jf");


            // MappingKit 生成器
            MappingKitGenerator mappingKitGenerator = new MappingKitGenerator(modelPackageName, baseModelOutputDir);
            mappingKitGenerator.setTemplate("yun/web/activerecord/dialect/pg/mapping_kit_template.jf");

            // 字典生成器
            PGDataDictionaryGenerator pgDataDictionaryGenerator = new PGDataDictionaryGenerator(ds, modelGenerator.getModelOutputDir());

            // 元信息
            JSONObject schemas = it.getJSONObject("schemas");
            //
            PGMetaBuilder pgMetaBuilder = new PGMetaBuilder(ds, schemas);
            // 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user"，移除前缀 "osc_"后生成的model名为 "User"而非 OscUser
            String tablePrefix = it.getString("table_prefix");
            pgMetaBuilder.setRemovedTableNamePrefixes(tablePrefix);


            Generator gen = new Generator(ds, pgBaseModelGenerator, modelGenerator);
            gen.setMappingKitGenerator(mappingKitGenerator);
            gen.setDataDictionaryGenerator(pgDataDictionaryGenerator);
            gen.setMetaBuilder(pgMetaBuilder);
            gen.setMappingKitPackageName(modelPackageName);

            //指定base_model_template_path 模板路径
            String base_model_template_path = it.getString("base_model_template_path");
            if (base_model_template_path != null) {
                gen.setBaseModelTemplate(base_model_template_path);
            }

            // 设置数据库方言
            gen.setDialect(new PostgreSqlDialect());

            // 添加不需要生成的表名
            //val toTypedArray = it.getJSONArray("excluded_tables").toJavaList(String::class.java).toTypedArray()
            //val toTypedArray = it.getArray<String>("excluded_tables")
            //gen.addExcludedTable(*toTypedArray)

            // 设置是否在 Model 中生成 dao 对象
            gen.setGenerateDaoInModel(true);

            // 设置是否生成字典文件
            gen.setGenerateDataDictionary(true);

            //允许model setter返回this
            gen.setGenerateChainSetter(true);

            // 生成
            gen.generate();
    }


    static DruidPlugin getDruidPlugin(JSONObject it) {
        String url = it.getString("url");
        String username = it.getString("username");
        String password = it.getString("password");

        //加载德鲁伊插件
        DruidPlugin dp = new DruidPlugin(url, username, password);

        //设置最大连接数与初始化连接数
        int max_active = it.getIntValue("max_active");
        int initial_size = it.getIntValue("initial_size");
        int min_idle = it.getIntValue("min_idle");

        dp.setMaxActive(max_active);
        dp.setInitialSize(initial_size);
        dp.setMinIdle(min_idle);
        // 加强数据库安全,防御SQL注入攻击
        // 由于insert <- select语法和 returning id 两处都被wf拦截掉, 弃用之
        /*val wallFilter = WallFilter();
        wallFilter.setDbType(JdbcConstants.ORACLE);
        druidPlugin.addFilter(wallFilter);*/

        // 添加 StatFilter 才会有统计数据, sql统计监控有pg_stat
        //druidPlugin.addFilter(StatFilter())
        // druidPlugin.setLogAbandoned(true)
        return dp;
    }

}